home *** CD-ROM | disk | FTP | other *** search
- unit Clock;
- {
- Author: Trevor J Carlsen
- Purpose: Demonstrate a simple "on screen" clock.
-
- This demo unit works by "hooking" the timer interrupt ($1c). This
- interrupt is called by the hardware interrupt ($08) approximately 18.2
- times every second and normally consists of a simple return instruction
- unless some other application has already hooked it.
-
- Because the routine is called roughly 18 times every second it is
- important that any processing it contains is optimised as much as
- possible. Obviously the best way to do this is by assembly language but
- in this demo I have used almost pure Turbo Pascal and have set up a
- counter variable and any processing is only done every 6 calls. This is
- more than sufficient and minimises processing. The routine is by no
- means perfect - there will be a minor irregularity for the final 10
- seconds of each day and for about half a second each hour. Better this
- than to waste valuable processing time in the interrupt by coding it
- out.
-
- Because DOS is not re-entrant it is also important that the routine make
- no calls to any procedure or function that makes use of dos for its
- operation. Thus no writeln, write can be used. To display the time on
- screen an array is addressed directly to screen memory. Any changes in
- this array are thus reflected on the screen. The downside to this is
- that on older CGAs this would cause a "snow" effect and code would be
- needed to eliminate this. It also means that the TP procedure GetTime
- cannot be used. So the time is calculated from the value stored at the
- clock tick counter location.
-
- To display an on-screen clock all that is required is for a programmer
- to include this unit in the uses declaration of the program.}
-
- interface
-
- const
- DisplayClock : boolean = true;
-
- implementation
- { Everything is private to this unit }
-
- uses dos;
-
- const
- line = 0; { Change as required for position of display on screen }
- column = 72; { Top left corner is 0,0 }
- ScreenPos = (line * 160) + (column * 2);
- Colour = $1f; { White on Blue }
- ZeroChar = Colour shl 8 + ord('0');
- Colon = Colour shl 8 + ord(':');
- type
- timestr = array[0..7] of word;
- timeptr = ^timestr;
- var
- time : timeptr;
- OldInt1c : pointer;
- ExitSave : pointer;
-
- {$F+}
- procedure Int1cISR; interrupt;
- { This will be called every clock tick by hardware interrupt $08 }
- const
- count : integer = 0; { To keep track of our calls }
- var
- hr : word absolute $40:$6e;
- ticks : word absolute $40:$6c;
- { This location keeps the number of clock ticks since 00:00}
- min,
- sec : byte;
- seconds : word;
- begin
- asm cli end;
- if DisplayClock then begin
- inc(count);
- if count = 6 then { ticks to update the display } begin
- count := 0; { equality check and assignment faster than mod 9 }
- seconds := ticks * longint(10) div 182; { speed = no reals }
- min := (seconds div 60) mod 60;
- sec := seconds mod 60;
-
- { The following statements are what actually display the on-screen time}
-
- time^[0] := ZeroChar + (hr div 10); { first char of hours }
- time^[1] := ZeroChar + (hr mod 10); { second char of hours }
- time^[2] := Colon;
- time^[3] := ZeroChar + (min div 10); { first char of minutes }
- time^[4] := ZeroChar + (min mod 10); { second char of minutes }
- time^[5] := Colon;
- time^[6] := ZeroChar + (sec div 10); { first char of seconds }
- time^[7] := ZeroChar + (sec mod 10); { second char of seconds }
- end; { if count = 6 }
- end; { if DisplayClock }
- asm
- sti
- pushf { push flags to set up for IRET }
- call OldInt1c; { Call old ISR entry point }
- end;
- end; { Int1cISR }
-
- procedure ClockExitProc;
- { This procedure is VERY important as you have hooked the timer interrupt }
- { and therefore if this is omitted when the unit is terminated your }
- { system will crash in an unpredictable and possibly damaging way. }
- begin
- ExitProc := ExitSave;
- SetIntVec($1c,OldInt1c); { This "unhooks" the timer vector }
- end;
- {$F-}
-
- procedure Initialise;
- var
- mode : byte absolute $40:$49;
- begin
- if mode = 7 then { must be a mono adaptor }
- time := ptr($b000,ScreenPos)
- else { colour adaptor of some kind }
- time := ptr($b800,ScreenPos);
- GetIntVec($1c,OldInt1c); { Get old timer vector and save it }
- ExitSave := ExitProc; { Save old exit procedure }
- ExitProc := @ClockExitProc; { Setup a new exit procedure }
- SetIntVec($1c,@Int1cISR); { Hook the timer vector to the new procedure }
- end; { Initialise }
-
- begin
- Initialise;
- end.
-
-